#
# if you want the ram-disk device, define this to be the
# size in blocks.
#
RAMDISK = #-DRAMDISK=512

AS86	=as86 -0 -a		# 8086 汇编编译器和连接器 见后面的介绍。后带的参数含义分别是
LD86	=ld86 -0		# -0 生成 8086 目标程序； -a生成与 gas 和 gld 部分兼容的代码

AS	=gas				# GNU 汇编编译器 gas 和连接器 gld
LD	=gld

# 下一行是 GNU 连接器 gld 运行时用到的选项。 含义是
# -s 输出文件中省略所有的符号信息l
# -x 删除所有局部符号
# -M 表示 需要在标准输出设备（显示器）上打印连接映像（link map）
# 是指由链接程序产生的一种内存地址映像，其中列出了程序段装入到内存中的位置信息。具体来讲有这些信息
# a. 目标文件及符号信息映射到内存中的位置
# b. 公共符号如何放置
# c. 连接中包含的所有文件成员及其引用的符号
LDFLAGS	=-s -x -M
CC	=gcc $(RAMDISK)		# gcc 是 GNU C程序编译器。 对于 UNIX 类的脚本程序而言，在引用定义的标识符时，
						# 需要在前面加上 $ 符号并用括号括住标识符

# 下两行是 gcc 的选项。 前一行最后的 \ 符号表示下一行是续行。选项含义为：
# -Wall 打印所有警告信息
# -O 指示对代码进行优化
# -fstrength-reduce 是优化循环语句
# -mstring-insns 是 Linus 自己为 gcc 增加的选项，用于对字符串指令优化程序。可以去掉
CFLAGS	=-Wall -O -fstrength-reduce -fomit-frame-pointer \
-fcombine-regs -mstring-insns
# 下行 cpp 是gcc的前（预处理程序）。标志 -nostdinc 和 -Iinclude 的含义是不要搜索标准目录中的头文件
# 而是使用 -I 选项指定目录或者是在当前目录里搜索头文件
CPP	=cpp -nostdinc -Iinclude

#
# ROOT_DEV specifies the default root-device when making the image.
# This can be either FLOPPY, /dev/xxxx or empty, in which case the
# default of /dev/hd6 is used by 'build'.
#
# ROOT_DEV 指定在创建内核映像（image） 文件时所使用的默认跟文件系统所在的设备，这可以是软盘
# （Floppy） /dev/xxxx 或者干脆是空着， 空着时 build 程序（在 tools/目录中）就使用默认值 /dev/hd6
ROOT_DEV=/dev/hd6

# 下面是 kernel mm 和 fs 目录所产生的目标代码文件。为方面引用，用 ARCHIVES 标识符表示
ARCHIVES=kernel/kernel.o mm/mm.o fs/fs.o
# 块和字符设备库文件。.a表示该文件是归档文件，即包含许多可执行二进制代码子程序集合的库文件
# 通常是用 GNU 的 ar 程序生成。 ar 是 GNU 的二进制文件处理程序，用于创建 修改及从归档文件中抽取文件
DRIVERS =kernel/blk_drv/blk_drv.a kernel/chr_drv/chr_drv.a
# 数学运算库文件
MATH	=kernel/math/math.a
# 有 lib/ 目录中的文件所编译生成的通用库文件
LIBS	=lib/lib.a

# make老式的隐式后缀规则。 改行指示make利用下面的命令将所有的 .c 文件编译生成 .s 汇编程序。 ‘：’表示下面是该规则的命令
.c.s:
	$(CC) $(CFLAGS) \						# 指示 gcc 采用前面 CFLAGS 所指定的选项以及使用 include/ 目录中的头文件
	-nostdinc -Iinclude -S -o $*.s $<		# 在适当地编译后不进行汇编就停止（-S），从而产生与输入的各个C文件对应的汇编
											# 语言形式的代码文件。默认情况下所产生的汇编程序文件是原C文件名去掉.c加上.s
											# 后缀。 -o表示其后是输出文件的形式。其中$*.s是自动目标变量，$<代表第一个
											# 先决条件，这里符合条件 *.c 的文件

# 指示将所有.s汇编程序编译成.o目标文件。 下一行是实现该操作的具体命令，即使用gas将汇编程序编译成.o目标文件
.s.o:
	$(AS) -c -o $*.o $<						# gas 标志 -c 表示只编译或汇编但不作连接操作。
# 类似上面 *.c 文件 -> *.o 目标文件
.c.o:
	$(CC) $(CFLAGS) \
	-nostdinc -Iinclude -c -o $*.o $<		# 使用 gcc 将C语言文件编译成目标文件但不连接

# all 表示创建 Makefile 所知的最顶层目标。这里是Image文件。
all:	Image

# 下句说明目标（Image）是由分号后的4个元素产生，分别是 boot/中的bootsect和setup文件 tools/中的system和build文件
# 42，43行是执行的命令。第一行表示使用 tools 目录下的工具程序build将bootsect、setup和system文件以 $(ROOT_DEV)
# 为根文件系统设备组装成内核映像文件 Image。
Image: boot/bootsect boot/setup tools/system tools/build
	tools/build boot/bootsect boot/setup tools/system $(ROOT_DEV) > Image
	sync		# 迫使缓冲块数据立即写盘并更新超级块

# 下一行表示 disk 这个目标要由 Image 产生。命令 dd 为Unix标准命令：复制一个文件，根据选项进行转换和格式化
# bs= 表示一次读/写的字节数。 if= 表示输入的文件 of= 表示输出到的文件
# 这里的/dev/PS0 是指第一个软盘驱动器（设备文件）
disk: Image
	dd bs=8192 if=Image of=/dev/PS0

# 由 tools 目录下的build.c 程序生成执行程序build
tools/build: tools/build.c
	$(CC) $(CFLAGS) \
	-o tools/build tools/build.c	# 编译生成执行程序build的命令

# 利用上面给出的 .s.o 规则生成 head.o目标文件
boot/head.o: boot/head.s

# 下句表示 tools/system文件要由分号右边元素生成。 56-61行是生成system的具体命令。
# 最后的 > system.map 表示 gld 需要将连接映像重定向保存在 system.map 文件中。 关于system.map文件的用途参见注释后的说明
tools/system:	boot/head.o init/main.o \
		$(ARCHIVES) $(DRIVERS) $(MATH) $(LIBS)
	$(LD) $(LDFLAGS) boot/head.o init/main.o \
	$(ARCHIVES) \
	$(DRIVERS) \
	$(MATH) \
	$(LIBS) \
	-o tools/system > System.map

# 数学协处理函数文件 math.a 由下一行上的命令实现
kernel/math/math.a:
	(cd kernel/math; make)		# 进入 kernel/math 目录 运行 make 工具程序

# 块设备函数文件 blk_drv.a
kernel/blk_drv/blk_drv.a:
	(cd kernel/blk_drv; make)

# 字符设备文件 chr_drv.a
kernel/chr_drv/chr_drv.a:
	(cd kernel/chr_drv; make)

# 内核目标模块 kernel.o
kernel/kernel.o:
	(cd kernel; make)

# 内存管理模块 mm.o
mm/mm.o:
	(cd mm; make)

# 文件系统目标模块 fs.o
fs/fs.o:
	(cd fs; make)

# 库函数 lib.a
lib/lib.a:
	(cd lib; make)

boot/setup: boot/setup.s							# 这里开始的三行是 使用8086汇编和连接器对 setup.s
	$(AS86) -o boot/setup.o boot/setup.s			# 文件进行编译生成setup文件
	$(LD86) -s -o boot/setup boot/setup.o			# -s 选项表示要去除目标文件中的符号信息

boot/bootsect:	boot/bootsect.s						# 同上 生成 bootsect.o 磁盘引导块
	$(AS86) -o boot/bootsect.o boot/bootsect.s
	$(LD86) -s -o boot/bootsect boot/bootsect.o

# 下面4行的作用是在 bootsect.s 程序开头添加一行有关system文件长度的信息。方法是首先生成含有 syssize=system文件实际长度 一行信息
# 的tmp.s文件， 然后将 bootsect.s 文件添加在其后。取得system长度的方法是：首先利用命令ls对system文件进行长列表显示，用grep命令取得
# 列表行上文件字节数字段信息 并定向保存在tmp.s临时文件中。 cut命令用于剪切字符串，tr用于去除行尾的回车符
# 其中 （实际长度+15）/16用于获得用 节 表示的长度信息 一节=16字节
tmp.s:	boot/bootsect.s tools/system
	(echo -n "SYSSIZE = (";ls -l tools/system | grep system \
		| cut -c25-31 | tr '\012' ' '; echo "+ 15 ) / 16") > tmp.s
	cat boot/bootsect.s >> tmp.s

# 当执行命令 make clean 时 就会忽略下面的命令 去除所有编译连接生成的文件
# rm 是文件删除命令 选项 -f 含义是忽略不存在的文件，并且不显示删除信息
clean:
	rm -f Image System.map tmp_make core boot/bootsect boot/setup
	rm -f init/*.o tools/system tools/build boot/*.o
	(cd mm;make clean)			# 进入mm/目录；执行该目录Makefile文件中的clean规则
	(cd fs;make clean)
	(cd kernel;make clean)
	(cd lib;make clean)

# 下面该规则将首先执行上面的 clean 规则，然后对 linux/ 目录进行压缩，生成 backup.z 压缩文件
# cd.. 表示推到 linux/的上一级目录， tar cf -linux 表示将 tar 程序的执行通过管道操作 | 传递给压缩程序 compress
# 并将压缩程序的输出存成 backup.Z 文件
backup: clean
	(cd .. ; tar cf - linux | compress - > backup.Z)
	sync				# 迫使缓冲块数据立即写盘并更新磁盘超级块

# 下面目标或规则用于各文件之间的依赖关系。创建的这些依赖关系是为了让 make 确定是否需要重建一个目标对象
# 比如当某个头文件被改动过后，make 就通过生成的依赖关系 重新编译与该头文件有关的所有 *.c 文件。
# 具体方法如下： 使用字符串编辑程序 sed 对 Makefile 文件（这里指自己）进行处理，输出为删除 Makefile 文件中的 ### Dependencies
# 行后的所有行 并生成 tmp_make 临时文件 然后对 init/ 目录下的每一个C文件（其实只有一个文件main.c）执行 gcc 预处理操作， -M标志告诉
# 预处理程序输出描述每个目标文件相关性的规则，并且这些规则符合make语法。对于每一个源文件，预处理程序输出一个make规则，其结果形式是相应
# 源程序文件的目标文件名加上其依赖关系-该源文件中包含的所有头文件列表。
# $$i 实际上是 $（$i）的意思。这里的$i是这句前面的 shell 变量的值。最后把预处理结果都添加到临时文件 tmp_make中
# 并将该临时文件复制成新的 Makefile 文件。
dep:
	sed '/\#\#\# Dependencies/q' < Makefile > tmp_make
	(for i in init/*.c;do echo -n "init/";$(CPP) -M $$i;done) >> tmp_make
	cp tmp_make Makefile
	(cd fs; make dep)
	(cd kernel; make dep)
	(cd mm; make dep)

### Dependencies:
init/main.o : init/main.c include/unistd.h include/sys/stat.h \
  include/sys/types.h include/sys/times.h include/sys/utsname.h \
  include/utime.h include/time.h include/linux/tty.h include/termios.h \
  include/linux/sched.h include/linux/head.h include/linux/fs.h \
  include/linux/mm.h include/signal.h include/asm/system.h include/asm/io.h \
  include/stddef.h include/stdarg.h include/fcntl.h 
